#include "StdAfx.h"
#include "TIFFSplitter.h"
#include "TiffLogger.h"

#include "resource.h"
#include "MFCTiffMergerDlg.h"

TIFFSplitter::TIFFSplitter(void)
{
}

list<CString> TIFFSplitter::tobeSplitted;

TIFFSplitter::~TIFFSplitter(void)
{
}

CString TIFFSplitter::getFilePathWithoutName(const CString &filePath)
{
	CString dir(L"");
	int index = filePath.ReverseFind(L'\\');
	if (index == -1) {
		index = filePath.ReverseFind(L'/');
		if (index == -1) {
			return dir;
		}
	}

	return filePath.Left(index + 1);
}

bool TIFFSplitter::splitTIFF(const CString& filePath)
{
	if (filePath.IsEmpty())
		return false;

	CPath path(filePath);
	path.RemoveFileSpec();
	if (!path.IsDirectory())
		return false;
		
	TIFF* input;
	if ((input = TIFFOpenW(filePath, "r")) == NULL) {
		return false;
	}

	CPath p(filePath);
	p.StripPath();
	p.RemoveExtension();

	bool result = extractTIFF(path, p, input);
	
	TIFFClose(input);


	return result;
}

void TIFFSplitter::updateSplittedFileNamesListBox(const CString &name)
{
	CMFCTiffMergerDlg* dlg = (CMFCTiffMergerDlg*)AfxGetApp()->GetMainWnd();
	CListBox* splittedFileNames = (CListBox*)dlg->m_page2.GetDlgItem(IDC_SPLITTED_FILEPATH_LIST);

	splittedFileNames->AddString(name);
	// Set the focus the last added item
	splittedFileNames->SetTopIndex(splittedFileNames->GetCount() - 1);
	splittedFileNames->UpdateWindow();
	dlg->m_page2.PostMessageW(WM_UPDATE_USER1, 0, 0);	
}

bool TIFFSplitter::extractTIFF(const CPath &dir, const CString &fileName, TIFF *input)
{
	int dirCount = 1;	
	uint16 spp;	
	do {
		TIFFGetField(input, TIFFTAG_SAMPLESPERPIXEL, &spp);	
		CPath temp = dir;
		temp.AddBackslash();
		temp.Append(fileName);
		temp.AddBackslash();

		if (_wmkdir((CString)temp)) {
			if(errno == ENOENT) {
				Log(LOG_ERROR, L"failed to create %s directories to contained the generated tiff flies\n", fileName);
				return false;
			}
		}

		CString format(L"");
		format.AppendFormat(L"%04d.tif", dirCount);
		temp.Append(format);
		
		if (spp == 1) {
			if (!extractNonColorImage(temp, input)) {
				return false;
			}
		} else {
			if (!extractColorImage(temp, input)) {
				return false;
			}
		}

		updateSplittedFileNamesListBox(temp);

		dirCount++;
	} while (TIFFReadDirectory(input));

	return true;
}


bool TIFFSplitter::extractNonColorImage(const CString &dir, TIFF *input)
{
	TIFF *output;
	if ((output = TIFFOpenW(dir, "w")) == NULL) {
		Log(LOG_ERROR, "failed to read input tiff files\n");		
		return false;
	}

	uint32 width, height;	
	float xres, yres;
	uint16 resUnit, bpp, spp, compression;

	TIFFGetField(input, TIFFTAG_IMAGEWIDTH, &width);
	TIFFGetField(input, TIFFTAG_IMAGELENGTH, &height);
	TIFFGetField(input, TIFFTAG_XRESOLUTION, &xres);
	TIFFGetField(input, TIFFTAG_YRESOLUTION, &yres);
	TIFFGetField(input, TIFFTAG_RESOLUTIONUNIT, &resUnit);
	TIFFGetField(input, TIFFTAG_BITSPERSAMPLE, &bpp);
	TIFFGetField(input, TIFFTAG_SAMPLESPERPIXEL, &spp);
	TIFFGetField(input, TIFFTAG_COMPRESSION, &compression);

	char *raster;
	int stripMax;
	unsigned long bufferSize, imageOffset;
	tsize_t stripSize, result;

	stripSize = TIFFStripSize(input);
	stripMax = TIFFNumberOfStrips(input);
	bufferSize = stripMax * stripSize;
	raster = new char[bufferSize];

	bool errorOccurred = false;
    imageOffset = 0;
	for (int stripCount = 0; stripCount < stripMax; stripCount++) {
		if ((result = TIFFReadEncodedStrip(input, stripCount, raster + imageOffset, stripSize)) == 0 ) {
            errorOccurred = true;
            break;
		}
		imageOffset += result;
	}
    
    if (errorOccurred) {
		Log(LOG_ERROR, "failed to read input tiff files\n");
        delete[] raster;        
        return false;
    }	
	
	// Write the tiff tags to the corresponding directory
	TIFFSetField(output, TIFFTAG_IMAGEWIDTH, width);
	TIFFSetField(output, TIFFTAG_IMAGELENGTH, height);
	TIFFSetField(output, TIFFTAG_COMPRESSION, compression);
	TIFFSetField(output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);

	TIFFSetField(output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
	TIFFSetField(output, TIFFTAG_BITSPERSAMPLE, bpp);
	TIFFSetField(output, TIFFTAG_SAMPLESPERPIXEL, spp);

	TIFFSetField(output, TIFFTAG_XRESOLUTION, xres);
	TIFFSetField(output, TIFFTAG_YRESOLUTION, yres);
	TIFFSetField(output, TIFFTAG_RESOLUTIONUNIT, resUnit);


	char *makeName = 0, *softwareName = 0;

	if (TIFFGetField(input, TIFFTAG_MAKE, &makeName)) {
		TIFFSetField(output, TIFFTAG_MAKE, makeName);
	}
	
	if (TIFFGetField(input, TIFFTAG_SOFTWARE, &softwareName)) {
		TIFFSetField(output, TIFFTAG_SOFTWARE, softwareName);
	}

	TIFFWriteEncodedStrip(output, 0, raster, bufferSize);
	
	TIFFClose(output);
	delete[] raster;
	return true;
}

bool TIFFSplitter::extractColorImage(const CString &dir, TIFF *input)
{
	TIFF *output;
	if ((output = TIFFOpenW(dir, "w")) == NULL) {
		Log(LOG_ERROR, "failed to read input tiff files\n");		
		return false;
	}

	uint32 width, height, imagesize;	
    uint32 *content;
	
    char *raster;
	float xres, yres;
	uint16 resUnit, bpp, spp, compression;
	char *makeName = 0;
	char *softwareName = 0;
    TIFFGetField(input, TIFFTAG_IMAGEWIDTH, &width);
	TIFFGetField(input, TIFFTAG_IMAGELENGTH, &height);
	TIFFGetField(input, TIFFTAG_XRESOLUTION, &xres);
	TIFFGetField(input, TIFFTAG_YRESOLUTION, &yres);
	TIFFGetField(input, TIFFTAG_RESOLUTIONUNIT, &resUnit);
	TIFFGetField(input, TIFFTAG_BITSPERSAMPLE, &bpp);
	TIFFGetField(input, TIFFTAG_SAMPLESPERPIXEL, &spp);
	TIFFGetField(input, TIFFTAG_COMPRESSION, &compression);

	imagesize = width * height + 1;
	raster = new char[width * height * 3];
	content = new uint32[imagesize];

	// Write the tiff tags to the corresponding directory
	TIFFSetField(output, TIFFTAG_IMAGEWIDTH, width);
	TIFFSetField(output, TIFFTAG_IMAGELENGTH, height);
	
	TIFFSetField(output, TIFFTAG_COMPRESSION, compression);
	TIFFSetField(output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	TIFFSetField(output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
	TIFFSetField(output, TIFFTAG_BITSPERSAMPLE, bpp);
	TIFFSetField(output, TIFFTAG_SAMPLESPERPIXEL, spp);

	TIFFSetField(output, TIFFTAG_XRESOLUTION, xres);
	TIFFSetField(output, TIFFTAG_YRESOLUTION, yres);
	TIFFSetField(output, TIFFTAG_RESOLUTIONUNIT, resUnit);

	if (TIFFGetField(input, TIFFTAG_MAKE, &makeName)) {
		TIFFSetField(output, TIFFTAG_MAKE, makeName);
	}
	
	if (TIFFGetField(input, TIFFTAG_SOFTWARE, &softwareName)) {
		TIFFSetField(output, TIFFTAG_SOFTWARE, softwareName);
	}

	uint32 e = 0, c = 0;
	if ((TIFFReadRGBAImage(input, width, height, content)) != 0) {
		for (e = 0; e < height; e++) {
			for (c = 0; c < width; c++) {
				raster[(e * width  + c) * 3 + 0] = TIFFGetR(content[(height - e - 1) * width + c]);
				raster[(e * width  + c) * 3 + 1] = TIFFGetG(content[(height - e - 1) * width + c]);
				raster[(e * width  + c) * 3 + 2] = TIFFGetB(content[(height - e - 1) * width + c]);
			}
		}						
	}

	TIFFWriteEncodedStrip(output, 0, raster, width * height * 3);

	delete[] raster;
	delete[] content;

	TIFFClose(output);

	return true;
}

bool TIFFSplitter::splitTIFFWithinDirectory(const CString& dirPath)
{
	recursiveCollectToBeSplittedFiles(dirPath);
	CMFCTiffMergerDlg* dlg = (CMFCTiffMergerDlg*)AfxGetApp()->GetMainWnd();	
	CButton *isDeleteOriginCheckBox = (CButton *)dlg->m_page2.GetDlgItem(IDC_DELETE_ORIGIN_CHECK);
	list<CString>::const_iterator iter = tobeSplitted.begin();
	while (iter != tobeSplitted.end()) {
		splitTIFF((*iter));

		if (isDeleteOriginCheckBox->GetCheck()) {
			deleteOriginFiles(*iter);
		}

		++iter;
	}

	tobeSplitted.clear();
	return true;
}

void TIFFSplitter::deleteOriginFiles(const CString &filePath)
{
	::DeleteFileW(filePath);
}

void TIFFSplitter::collectToBeSplittedFiles(const CString &dirPath)
{
	CFileFind finder;
	CPath dir(dirPath);
	dir.AddBackslash();
	dir.Append(L"*");

	if (finder.FindFile(dir)) {
		BOOL bWorking = TRUE;
		while (bWorking) {			
			bWorking = finder.FindNextFileW();
			if (finder.IsDots() || finder.IsDirectory())
				continue;
			CString alternative = finder.GetFilePath();
            if (isTiffFiles(finder.GetFileName())) {
				tobeSplitted.push_back(alternative);
            }
          
		}
		finder.Close();
	}
}

void TIFFSplitter::recursiveCollectToBeSplittedFiles(const CString& dirPath)
{
	CFileFind finder;
	CPath dir(dirPath);
	dir.AddBackslash();
	dir.Append(L"*");

	if (finder.FindFile(dir)) {
		BOOL bWorking = TRUE;
		while (bWorking) {			
			bWorking = finder.FindNextFileW();
			if (finder.IsDots())
				continue;
			if (finder.IsDirectory()) {
				recursiveCollectToBeSplittedFiles(finder.GetFilePath());
			}

			CString alternative = finder.GetFilePath();
            if (isTiffFiles(finder.GetFileName())) {
				tobeSplitted.push_back(alternative);
            }
          
		}
		finder.Close();
	}
}

bool TIFFSplitter::isTiffFiles(const CString &fileName)
{
	CPath p(fileName);
	CString extension = p.GetExtension();
	if (!extension.CompareNoCase(_T("tiff")) || !extension.CompareNoCase(_T("tif"))) {
        return true;
    }
}

bool TIFFSplitter::splitTIFF(const CString &fileSpecPath, const CString &filePath, int &splittedCount)
{		
	TIFF* input;
	if ((input = TIFFOpenW(filePath, "r")) == NULL) {
		return false;
	}

	CPath fileSpec(fileSpecPath);

	bool result = extractTIFF(fileSpec, input, splittedCount);
	
	TIFFClose(input);
	return result;
}

bool TIFFSplitter::extractTIFF(const CPath &fileSpecPath, TIFF *input, int &splittedCount)
{	
	uint16 spp;
	TIFFGetField(input, TIFFTAG_SAMPLESPERPIXEL, &spp);	
	do {
		CPath temp = fileSpecPath;
		temp.AddBackslash();
		CString format(L"");		
		format.AppendFormat(L"%04d.tiff", splittedCount);
		temp.Append(format);
		
		if (spp == 1) {
			if (!extractNonColorImage(temp, input)) {
				return false;
			}
		} else {
			if (!extractColorImage(temp, input)) {
				return false;
			}
		}

		updateSplittedFileNamesListBox(temp);

		splittedCount++;
	} while (TIFFReadDirectory(input));

	return true;
}